/************************************************************************

							Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  							   streambuf_operators.h

	$Header: heroes4/resource_editor/streambuf_operators.h $

	$NoKeywords: $

 ************************************************************************/
#ifndef STREAMBUF_OPERATORS_H_INCLUDED
#define STREAMBUF_OPERATORS_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <exception>
#include <ios>
#include <streambuf>

#include "sized_int_types.h"

// -----------------------------------------------------------------
// t_streambuf_read_failure exception class
// -----------------------------------------------------------------

class t_streambuf_read_failure : public std::exception
{
public:
	// Member functions
	virtual char const * what() const throw();
};

// -----------------------------------------------------------------
// t_streambuf_write_failure exception class
// -----------------------------------------------------------------

class t_streambuf_write_failure : public std::exception
{
public:
	// Member functions
	virtual char const * what() const throw();
};

// -----------------------------------------------------------------
// functions to allow rational read / writes without extra typecasts
// -----------------------------------------------------------------

inline int read( std::streambuf& buffer, void* arg, int length )
{
	return buffer.sgetn( reinterpret_cast<char*>(arg), length );
}

inline int write( std::streambuf& buffer, void const* arg, int length )
{
	return buffer.sputn( reinterpret_cast<char const*>(arg), length );
}

// -----------------------------------------------------------------
// Convenience function templates for reading and writing simple
// types
// -----------------------------------------------------------------

template < typename t_arg >
inline void put( std::streambuf & buffer, t_arg const & arg )
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
}

template < typename t_result >
inline t_result get( std::streambuf & buffer )
{
	t_result result;
	if ( read( buffer, &result, sizeof( result ) ) != sizeof( result ) )
		throw t_streambuf_read_failure();
	return result;
}

// -----------------------------------------------------------------
// string I/O functions
// -----------------------------------------------------------------

bool read_string16( std::streambuf& buffer, std::string& arg );

inline bool write_string16( std::streambuf& buffer, std::string const& arg )
{
	t_uint16 size = arg.size();
	return		write( buffer, &size, sizeof( size ) ) == sizeof( size )
			&&	write( buffer, arg.data(), size ) == size;
}

// -----------------------------------------------------------------
// inlined input / output operators for basic types.
// -----------------------------------------------------------------

inline std::streambuf& operator<<(std::streambuf& buffer, bool const& arg)
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf& operator>>(std::streambuf& buffer, bool& arg)
{
	if ( read( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}

inline std::streambuf& operator<<(std::streambuf& buffer, int const& arg)
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf& operator>>(std::streambuf& buffer, int& arg)
{
	if ( read( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}

inline std::streambuf& operator<<(std::streambuf& buffer, unsigned int const& arg)
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf& operator>>(std::streambuf& buffer, unsigned int& arg)
{
	if ( read( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}

inline std::streambuf& operator<<(std::streambuf& buffer, t_int8 const& arg)
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf& operator>>(std::streambuf& buffer, t_int8& arg)
{
	if ( read( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}

inline std::streambuf& operator<<(std::streambuf& buffer, t_uint8 const& arg)
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf& operator>>(std::streambuf& buffer, t_uint8& arg)
{
	if ( read( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}

inline std::streambuf& operator<<(std::streambuf& buffer, t_int16 const& arg)
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf& operator>>(std::streambuf& buffer, t_int16& arg)
{
	if ( read( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}

inline std::streambuf& operator<<(std::streambuf& buffer, t_uint16 const& arg)
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf& operator>>(std::streambuf& buffer, t_uint16& arg)
{
	if ( read( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}

inline std::streambuf& operator<<(std::streambuf& buffer, t_int32 const& arg)
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf& operator>>(std::streambuf& buffer, t_int32& arg)
{
	if ( read( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}

inline std::streambuf& operator<<(std::streambuf& buffer, t_uint32 const& arg)
{
	if ( write( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf& operator>>(std::streambuf& buffer, t_uint32& arg)
{
	if ( read( buffer, &arg, sizeof( arg ) ) != sizeof( arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}

inline std::streambuf & operator<<( std::streambuf & buffer, std::string const & arg )
{
	if ( !write_string16( buffer, arg ) )
		throw t_streambuf_write_failure();
	return buffer;
}

inline std::streambuf & operator>>( std::streambuf & buffer, std::string & arg )
{
	if ( !read_string16( buffer, arg ) )
		throw t_streambuf_read_failure();
	return buffer;
}


// the following is required because pubseekoff returns a non-int type, and 
// frequent typecasts are required if it is used in expressions.
inline int tell( std::streambuf& buffer )
{
	return buffer.pubseekoff( 0, std::ios::cur );
}

#endif // ifndef STREAMBUF_OPERATORS_H_INCLUDED
